<template>
  <div class="audition251">
    <v-md-preview :text="text1" />
    <p>推荐阅读：<a href="https://zhuanlan.zhihu.com/p/183801144" target="_blank">手写Promise</a></p>
  </div>
</template>

<script>
const prefix = '``` js'
const suffix = '```'
export default {
  name: 'Audition251',
  data() {
    return {
      text1: `${prefix}
      const resolvePromise = (p, x, resolve, reject) => {
        if(p === x) {
          return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
        }
        let called
        if((typeof x === 'object' && x !== null) || typeof x === 'function') {
          try {
            let then = x.then
            if(typeof then === 'function') {
              then.call(x, y => {
                if(called) return
                called = true
                resolvePromise(p, y, resolve, reject)
              }, r => {
                if(called) return
                called = true
                reject(r)
              })
            } else {
              resolve(x)
            }
          } catch(err) {
            if(called) return
            called = true
            reject(err)
          }
        } else {
          resolve(x)
        }
      }
      class _Promise {
        constructor(executor) {
          this.state = 'pending'
          this.value = undefined
          this.reason = undefined
          this.onResolvedCallbacks = []
          this.onRejectedCallbacks = []

          let resolve = (value) => {
            if (this.state === 'pending') {
              this.state = 'fulfilled'
              this.value = value
              this.onResolvedCallbacks.forEach(cb => cb())
            }
          }

          let reject = (reason) => {
            if (this.state === 'pending') {
              this.state = 'rejected'
              this.reason = reason
              this.onRejectedCallbacks.forEach(cb => cb())
            }
          }

          try {
            executor(resolve, reject)
          } catch(err) {
            reject(err)
          }
        }

        then(onFulfilled, onRejected) {
          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
          onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
          let p = new _Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
              setTimeout(() => {
                try {
                  let x = onFulfilled(this.value)
                  resolvePromise(p, x, resolve, reject)
                } catch(err) {
                  reject(err)
                }
              }, 0)
            }
            if (this.state === 'rejected') {
              setTimeout(() => {
                try {
                  let x = onRejected(this.reason)
                  resolvePromise(p, x, resolve, reject)
                } catch(err) {
                  reject(err)
                }
              }, 0)
            }
            if (this.state === 'pending') {
              this.onResolvedCallbacks.push(() => {
                setTimeout(() => {
                  try {
                    let x = onFulfilled(this.value)
                    resolvePromise(p, x, resolve, reject)
                  } catch(err) {
                    reject(err)
                  }
                })
              }, 0)
            }
            this.onRejectedCallbacks.push(() => {
              setTimeout(() => {
                try {
                  let x = onRejected(this.reason)
                  resolvePromise(p, x, resolve, reject)
                } catch(err) {
                  reject(err)
                }
              }, 0)
            })
          })

          return p
        }
      }
      _Promise.prototype.all = function (values) {
        if(!Array.isArray(values)) {
          const type = typeof values
          return new TypeError('TypeError: ' + type + values + 'is not iterable')
        }
        return new _Promise((resolve, reject) => {
          let resultArr = []
          let orderIndex = 0
          const processResultByKey = (value, index) => {
            resultArr[index] = value
            if(++orderIndex === values.length) {
              resolve(resultArr)
            }
          }
          for(let i = 0; i < values.length; i++) {
            let value = values[i]
            if(value && typeof value.then === 'function') {
              value.then(value => {
                processResultByKey(value, i)
              }, reject)
            } else {
              processResultByKey(value, i)
            }
          }
        })
      }
      _Promise.prototype.race = function (promises) {
        return new _Promise((resolve, reject) => {
          for(let i = 0; i < promises.length; i++) {
            let val = promises[i]
            if(val && typeof val.then === 'function') {
              val.then(resolve, reject)
            } else {
              resolve(val)
            }
          }
        })
      }
      \n${suffix}`,
    }
  }
}
</script>

<style>

</style>